<?php

declare(strict_types=1);

/*
 * This file is part of PHP CS Fixer.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *     Dariusz Rumiński <dariusz.ruminski@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace PhpCsFixer\Tests\Fixer\Import;

use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
use PhpCsFixer\WhitespacesFixerConfig;

/**
 * @internal
 *
 * @covers \PhpCsFixer\Fixer\Import\SingleImportPerStatementFixer
 *
 * @extends AbstractFixerTestCase<\PhpCsFixer\Fixer\Import\SingleImportPerStatementFixer>
 *
 * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
 *
 * @phpstan-import-type _AutogeneratedInputConfiguration from \PhpCsFixer\Fixer\Import\SingleImportPerStatementFixer
 *
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
 */
final class SingleImportPerStatementFixerTest extends AbstractFixerTestCase
{
    /**
     * @param _AutogeneratedInputConfiguration $configuration
     *
     * @dataProvider provideFixCases
     */
    public function testFix(string $expected, ?string $input = null, array $configuration = [], bool $useTabsAndWindowsNewlines = false): void
    {
        $this->fixer->configure($configuration);
        if ($useTabsAndWindowsNewlines) {
            $this->fixer->setWhitespacesConfig(new WhitespacesFixerConfig("\t", "\r\n"));
        }
        $this->doTest($expected, $input);
    }

    /**
     * @return iterable<int, array{0: string, 1?: null|string, 2?: _AutogeneratedInputConfiguration}>
     */
    public static function provideFixCases(): iterable
    {
        yield [
            '<?php
                    /**/use Foo;
                    use FooB;
                ',
            '<?php
                    /**/use Foo,FooB;
                ',
        ];

        yield [
            <<<'EOF'
                use Some, Not, PHP, Like, Use, Statement;
                <?php

                use Foo;
                use FooA;
                use FooB;
                use FooC;
                use FooD as D;
                use FooE;
                use FooF;
                use FooG as G;
                use FooH;
                use FooI;
                use FooJ;
                use FooZ;

                EOF,
            <<<'EOF'
                use Some, Not, PHP, Like, Use, Statement;
                <?php

                use Foo;
                use FooA, FooB;
                use FooC, FooD as D, FooE;
                use FooF,
                    FooG as G,
                  FooH,     FooI,
                        FooJ;
                use FooZ;

                EOF,
        ];

        yield [
            <<<'EOF'
                <?php

                namespace {
                    use Foo;
                    use FooA;
                    use FooB;
                    use FooC;
                    use FooD as D;
                    use FooE;
                    use FooF;
                    use FooG as G;
                    use FooH;
                    use FooI;
                    use FooJ;
                    use FooZ;
                }

                namespace Boo {
                    use Bar;
                    use BarA;
                    use BarB;
                    use BarC;
                    use BarD as D;
                    use BarE;
                    use BarF;
                    use BarG as G;
                    use BarH;
                    use BarI;
                    use BarJ;
                    use BarZ;
                }

                EOF,
            <<<'EOF'
                <?php

                namespace {
                    use Foo;
                    use FooA, FooB;
                    use FooC, FooD as D, FooE;
                    use FooF,
                        FooG as G,
                      FooH,     FooI,
                            FooJ;
                    use FooZ;
                }

                namespace Boo {
                    use Bar;
                    use BarA, BarB;
                    use BarC, BarD as D, BarE;
                    use BarF,
                        BarG as G,
                      BarH,     BarI,
                            BarJ;
                    use BarZ;
                }

                EOF,
        ];

        yield [
            '<?php
                    use FooA;
                    use FooB;
                ',
            '<?php
                    use FooA, FooB;
                ',
        ];

        yield [
            '<?php use FooA;
use FooB?>',
            '<?php use FooA, FooB?>',
        ];

        yield [
            '<?php
use B;
use C;
    use E;
    use F;
        use G;
        use H;
',
            '<?php
use B,C;
    use E,F;
        use G,H;
',
        ];

        yield [
            '<?php
use B;
/*
*/use C;
',
            '<?php
use B,
/*
*/C;
',
        ];

        yield [
            '<?php
use A;
use B;
//,{} use ; :
#,{} use ; :
/*,{} use ; :*/
use C  ; ',
            '<?php
use A,B,
//,{} use ; :
#,{} use ; :
/*,{} use ; :*/
C  ; ',
        ];

        yield [
            '<?php use Z ;
use X ?><?php new X(); // run before white space around semicolon',
            '<?php use Z , X ?><?php new X(); // run before white space around semicolon',
        ];

        yield [
            '<?php use FooA#
;#
#
use FooB;',
            '<?php use FooA#
,#
#
FooB;',
        ];

        yield [
            '<?php use some\b\ClassB;
use function some\b\CC as C;
use function some\b\D;
use const some\b\E;
use function some\b\A\B;',
            '<?php use some\b\{ClassB, function CC as C, function D, const E, function A\B};',
        ];

        yield [
            '<?php
use Foo\Bar;
use Foo\Baz;',
            '<?php
use Foo\ {
    Bar, Baz
};',
        ];

        yield [
            '<?php
use Foo\Bar;
use Foo\Baz;',
            '<?php
use Foo\
{
    Bar, Baz
};',
        ];

        yield [
            '<?php
use function md5;
use function str_repeat;
use const true;
use const false;
use A;
use B;
',
            '<?php
use function md5, str_repeat;
use const true, false;
use A,B;
',
        ];

        yield [
            '<?php
use D\E;
use D\F;
use G\H;
use G\I/*1*//*2*/;
',
            '<?php
use D\{E,F,};
use G\{H,I/*1*/,/*2*/};
',
        ];

        yield [
            '<?php
use A\B;
',
            '<?php
use A\{B};
',
        ];

        yield [
            '<?php
use Space\Models\TestModelA;
use Space\Models\TestModelB;
use Space\Models\TestModel;',
            '<?php
use Space\Models\ {
    TestModelA,
    TestModelB,
    TestModel,
};',
        ];

        yield [
            '<?php
use Space\Models\ {
    TestModelA,
    TestModelB,
    TestModel,
};',
            null,
            ['group_to_single_imports' => false],
        ];

        yield [
            "<?php\r\n    use FooA;\r\n    use FooB;",
            "<?php\r\n    use FooA, FooB;",
            [],
            true,
        ];
    }

    /**
     * @dataProvider provideFixPre80Cases
     *
     * @requires PHP <8.0
     */
    public function testFixPre80(string $expected, string $input): void
    {
        $this->doTest($expected, $input);
    }

    /**
     * @return iterable<array{string, string}>
     */
    public static function provideFixPre80Cases(): iterable
    {
        yield [
            '<?php
use some\a\ClassA;
use some\a\ClassB;
use some\a\ClassC as C;
use function some\b\fn_a;
use function some\b\fn_b;
use function some\b\fn_c;
use const some\c\ConstA/**/as/**/E; /* group comment */
use const some\c\ConstB as D;
use const some\c\// use.,{}
ConstC;
use A\B;
use D\E;
use D\F;
                ',
            '<?php
use some\a\{ClassA, ClassB, ClassC as C};
use    function some\b\{fn_a, fn_b, fn_c};
use const/* group comment */some\c\{ConstA/**/as/**/ E   ,    ConstB   AS    D, '.'
// use.,{}
ConstC};
use A\{B};
use D\{E,F};
                ',
        ];

        yield 'messy comments' => [
            '<?php
use D\/*1*//*2*//*3*/E;
use D\/*4*//*5*//*6*//*7*//*8*//*9*/F/*10*//*11*//*12*/;
',
            '<?php
use D\{
/*1*//*2*//*3*/E,/*4*//*5*//*6*/
/*7*//*8*//*9*/F/*10*//*11*//*12*/
};
',
        ];
    }
}
